home *** CD-ROM | disk | FTP | other *** search
- /*
- File: UnitTableDeviceAccess.c
-
- Contains: All USB interface device access specific functions
-
- Version: 1.0
-
- Copyright: © 1998-2000 by Apple Computer, Inc., all rights reserved.
-
-
- */
-
- #include "UnitTableDeviceAccess.h"
-
- #include <DriverGestalt.h>
- #include <DriverServices.h>
- #include <USB.h>
-
- #include "USB_StdCommands.h"
- #include "StorageDeviceConfiguration.h"
- #include "USB_Version.h"
- #include "USB_ClassDriverAccess.h"
- #include "USB_ManualEjectSupport.h"
- #include "USB_ShimServicesSupport.h"
- #include "StorageClassPublicAPI.h"
-
- #pragma mark -
- #pragma mark Private Module Global Variables
- // USB specific module data
- static USBDeviceRef gDeviceUSBRefNum; // The device's USB Device Ref number
-
- // This is used by other modules -- needs a good home
- UInt8 gDeviceUSBSubClass;
-
- // Interface independent module data
- static RegEntryIDPtr gDeviceRegEntryIDPtr;
- static DriverRefNum gDrvrRefNum;
- static Str32 gDeviceWhereString;
- static SupportedMediaStruct gSupportedMediaTypes = { {0}, { 'aaaa','aaaa','aaaa','aaaa'}};
- static ReadCapacityData gCurrentMediaCapacity = {0,0};
- static Boolean gMediaIsWriteProtected = false;
- static Boolean gPreventCommandFailed = false;
- static UInt8 gCommandSetANSIVersion = 0;
- static UInt8 gPeripheralDeviceType = 0;
-
- // This flag indicates whether an eject sequence is in progress. This allows us
- // to repsond immendiately to an eject call and not force the client to have to wait
- // for the ejection to complete.
- static Boolean gWaitingForEjectCompletion = false;
-
- // Is the controlled device a fixed disk ( hard disk ) drive
- static Boolean gDriveIsFixed = false;
- static Boolean gRemountFixed = true;
-
- // Does the controlled device support floppy media?
- static Boolean gDriveSupportsFloppies = false;
-
- // Does the controlled device support non-floppy removable media (i.e. Zip, LS-120, Orb etc)?
- static Boolean gDriveSupportsNonFloppyRemovable = false;
-
- static DriverGestaltDeviceModelInfoResponse gDeviceInfoStruct;
-
- // Device Info strings
- static Str63 gVendorIDString;
- static Str63 gProductIDString;
- static Str63 gFWRevisionString;
- static Str63 gFWSubRevisionString;
- static Str63 gSerialNumberString;
-
- #pragma mark -
- #pragma mark Private Module Routines
- static OSStatus InitializeDriveAccess( IntDriveRequestPBPtr requestPB, void *dispatchPtr );
- static OSStatus TerminateDriveAccess ( void );
-
- static void ExternalRequestComplete ( IntDriveRequestPBPtr requestPB );
-
- // Completion routines for Read/Write requests.
- static void ReadWriteRequestComplete ( IntDriveRequestPBPtr requestPB );
- static void WriteRequestSenseComplete ( IntDriveRequestPBPtr requestPB );
- static void ReadWriteRequestSenseOnErrorComplete ( IntDriveRequestPBPtr requestPB );
-
- #pragma mark -
- #pragma mark External Query Functions
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // External Query Functions
- // These are all functions available externally for querying
- // specific information about the device and interface
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Boolean IsDeviceAccessEnabled( void )
- {
- if ( IsClassDispatchTableValid() == false )
- {
- return false;
- }
- else
- {
- Boolean result;
- OSStatus status;
- UInt32 classDriverStatus;
-
- status = SendClassStatusCall( kUSBStorageStatusGetServicesStatus, &classDriverStatus );
-
- if ( (classDriverStatus == kUSBStorageServicesConfigureComplete) && ( status == noErr ) )
- {
- result = true;
- }
- else
- {
- result = false;
- }
-
- return result;
- }
- }
-
- Boolean DoesInterfaceHaveVirtualMemoryCapabilities( void )
- {
- // Since USB needs software interrupt support, a USB device
- // can never be in the VM page map path and so the DAM will
- // always return false.
- return false;
- }
-
- // Accessor for the Pascal String of the InterfaceType
- StringPtr GetDeviceWhereString( void )
- {
- return gDeviceWhereString;
- }
-
- void GetMediaProperties ( UInt32 *TotalBlocksOnMedia, UInt32 *BlockLengthInBytes, Boolean *IsWriteProtected, OSType *currentType )
- {
- *TotalBlocksOnMedia = gCurrentMediaCapacity.TotalBlocksOnMedia; // Total number of blocks
- *BlockLengthInBytes = gCurrentMediaCapacity.BlockLengthInBytes; // Total number of blocks
- *IsWriteProtected = gMediaIsWriteProtected;
-
- if ( gDriveIsFixed == true )
- {
- *currentType = kdgDiskType;
- }
- else if ( gPeripheralDeviceType == kInqCDROM_MMCDevice )
- {
- *currentType = kdgCDType;
- }
- else
- {
- // This is a removable disk, set the default media type
- *currentType = kdgRemovableType;
- if ( gDriveSupportsFloppies == true )
- {
- // If device supports floppies, and this is one
- // return the correct media type
- if( gCurrentMediaCapacity.TotalBlocksOnMedia <= 0x0C00L)
- {
- *currentType = kdgFloppyType;
- }
- }
- }
- }
-
- // Accessor for Device Ref number
- UInt32 GetDeviceReferenceNumber( void )
- {
- return ((UInt32) gDeviceUSBRefNum);
- }
-
- // Accessor for the DriverGestalt InterfaceType
- OSType GetDriverGestaltIntfForInterface( void )
- {
- return kdgUSBIntf;
- }
-
- // Accessor for the DeviceInfo struct
- DriverGestaltDeviceModelInfoResponse *GetDeviceInfoPtr( void )
- {
- return &gDeviceInfoStruct;
- }
-
- void GetDriverVersionNumber( NumVersion *theVersion )
- {
- theVersion->majorRev = kStorageHexMajorVers;
- theVersion->minorAndBugRev = kStorageHexMinorVers;
- theVersion->stage = kStorageReleaseStage;
- theVersion->nonRelRev = kStorageCurrentRelease;
- }
-
- // Accessor for the Supported Media types struct
- DriverGestaltSupportedMediaTypesResponse *GetSupportedMediaTypesPtr( void )
- {
- return (DriverGestaltSupportedMediaTypesResponse *) &gSupportedMediaTypes;
- }
-
- #pragma mark -
- #pragma mark Initialize/Terminate DAM Functions
- // Setup the access to the drive if the dispatchPtr is nil just return
- // and wait for this function to be called with a valid dispatchPtr.
- // -- For Interfaces that don't need a dispatch table setup will initialize
- // the Device access regardless of what is passed for dispatchPtr
- OSStatus InitializeDeviceAccess( DriverRefNum theRefNum, RegEntryIDPtr theRegEntryPtr )
- {
- Str32 tempStr;
-
- gDrvrRefNum = theRefNum;
- gDeviceRegEntryIDPtr = theRegEntryPtr;
-
- gDeviceInfoStruct.infoStructVersion = kInfoStructStringPtrsVers1;
- gDeviceInfoStruct.vendorName = nil;
- gDeviceInfoStruct.productName = nil;
- gDeviceInfoStruct.revisionNumber = nil;
- gDeviceInfoStruct.subRevisionNumber = nil;
- gDeviceInfoStruct.serialNumber = nil;
-
- // Build the Drive Info string returned in Status/Control calls
- PStrCopy( gDeviceWhereString, "\pUSB (v");
- CStrToPStr(tempStr, kStorageStringVersShort); // driver version
- PStrCat( gDeviceWhereString, tempStr); // driver version
- PStrCat( gDeviceWhereString, "\p)");
-
- return noErr;
- }
-
- OSStatus TerminateDeviceAccess( DriverRefNum theRefNum, RegEntryIDPtr theRegEntryPtr )
- {
- #pragma unused ( theRefNum, theRegEntryPtr )
-
- return TerminateDriveAccess();
- }
-
- #pragma mark -
- #pragma mark Control and Status
- OSStatus HandleControlRequest ( UInt32 userData, CntrlParamPtr cntrlPBPtr, ControlStatusCompletionProcPtr callBack )
- {
- OSStatus err;
-
- switch(cntrlPBPtr->csCode)
- {
- // This control call is made by the Shim to inform the driver of the dispatch table for
- // its class driver.
- case kInitializeDeviceAccess:
- {
- IntDriveRequestPBPtr ourPB;
-
- ourPB = GetDriveInternalPB();
-
- ourPB->userData = userData;
- ourPB->userCompletionProc = callBack;
-
- //SysDebugStr("\pInit Drive Access");
- err = InitializeDriveAccess( ourPB, (void *) *((UInt32 *)(&cntrlPBPtr->csParam[0])));
- if ( err != kRequestPending )
- {
- //SysDebugStr("\pInit Drive Access Error occurred");
- FreeInternalPB( ourPB );
- if (( err != noErr ) && ( err != kClassNotConfiguredError ))
- {
- // An error occurred and it was not Class not configured
- // Terminate the driver and allow the class to be replaced.
- // We do not care about an errors from Terminate and will return
- // the error previously returned from Initialize.
- (void) TerminateDriveAccess();
- }
- }
- }
- break;
-
- case kTerminateDeviceAccess:
- {
- // This control call is made by the Shim to inform the driver that the dispatch table is
- // no longer valid.
- err = TerminateDriveAccess();
- }
- break;
-
- default:
- {
- err = controlErr;
- }
- }
-
- return err;
- }
-
- OSStatus HandleStatusRequest ( UInt32 userData, CntrlParamPtr cntrlPBPtr, ControlStatusCompletionProcPtr callBack )
- {
- #pragma unused ( userData, callBack )
- OSStatus err = noErr;
-
- // Handle the status call
- switch(cntrlPBPtr->csCode)
- {
- default:
- {
- err = statusErr;
- }
- break;
- }
-
- return err;
- }
-
- #pragma mark -
- #pragma mark Initialize Drive Access
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Initialize Drive Access
- // These are all functions and definitions used for handling
- // the initialization of drive access
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- enum
- {
- kInitStartState = 1,
- kInitTURDoneState,
- kInitReqSenseDoneState,
- kInitGetInquirySizeDoneState,
- kInitInquiryDoneState
- };
-
- static void InitStateMachine( IntDriveRequestPBPtr ourPB );
-
- OSStatus InitializeDriveAccess( IntDriveRequestPBPtr requestPB, void *dispatchPtr )
- {
- USBStorageClassSetupTablePtr theSetupTablePtr;
-
- if ( dispatchPtr == nil )
- {
- return noErr;
- }
-
- theSetupTablePtr = (USBStorageClassSetupTablePtr) dispatchPtr;
-
- gDeviceUSBRefNum = theSetupTablePtr->usbDeviceRef;
- gDeviceUSBSubClass = theSetupTablePtr->usbSubClass;
- SetShimDispatchTable( gDrvrRefNum, theSetupTablePtr->shimDispatchTable );
- SetClassDispatchTable( theSetupTablePtr->theStorageClassDispatchTable );
-
- gSupportedMediaTypes.supportTypesCount = 0;
-
- if( IsClassDispatchTableValid() == true )
- {
- // Initalize the Class Driver
- (void) InitializeClassAccess();
- }
- else
- {
- return kClassNotConfiguredError;
- }
-
- if ( IsDeviceAccessEnabled() == true )
- {
- StringPtr getClassStringPtr;
- OSStatus status;
-
- // Get the Vendor String from the class driver and copy it here
- status = SendClassStatusCall(kUSBStorageStatusGetVendorStringPtr, &getClassStringPtr);
- if( status == noErr )
- {
- if ( getClassStringPtr != nil )
- {
- PStrCopy( gVendorIDString, getClassStringPtr);
- gDeviceInfoStruct.vendorName = gVendorIDString;
-
- }
- }
-
- // Get the Product String from the class driver and copy it here
- status = SendClassStatusCall(kUSBStorageStatusGetProductStringPtr, &getClassStringPtr);
- if( status == noErr )
- {
- if ( getClassStringPtr != nil )
- {
- PStrCopy( gProductIDString, getClassStringPtr);
- gDeviceInfoStruct.productName = gProductIDString;
-
- }
- }
-
- status = SendClassControlCall(kUSBStorageControlSetParentsRefNumber, &theSetupTablePtr->usbParentRef);
-
- requestPB->currentExecutionState = kInitStartState;
- requestPB->completionProc = &InitStateMachine;
- requestPB->status = noErr;
- InitStateMachine( requestPB );
-
- return kRequestPending;
- }
-
- return kClassNotConfiguredError;
- }
-
- static OSStatus ProcessInquiryData( UInt8 *InquiryBuffer );
-
- void InitStateMachine( IntDriveRequestPBPtr ourPB )
- {
- OSStatus err = ourPB->status;
-
- switch( ourPB->currentExecutionState )
- {
- case kInitStartState:
- {
- // Let's do a TUR and Request sense to make sure the drive is in
- // a good state
- ourPB->currentExecutionState = kInitTURDoneState;
- ourPB->completionProc = &InitStateMachine;
- err = TestUnitReadyBuilder( ourPB );
- }
- break;
-
- case kInitTURDoneState:
- {
- ourPB->currentExecutionState = kInitReqSenseDoneState;
- ourPB->completionProc = &InitStateMachine;
- err = RequestSenseBuilder( ourPB, (Ptr) ourPB->buffer );
- }
- break;
-
- case kInitReqSenseDoneState:
- {
- FixRequestSenseData( ourPB );
- err = ourPB->status;
- if ( err == noErr )
- {
- UInt8 inquiryCount;
-
- ourPB->currentExecutionState = kInitGetInquirySizeDoneState;
- ourPB->completionProc = &InitStateMachine;
- inquiryCount = kDeviceMaxInquiryCount;
-
- if (( inquiryCount > 6 ) || ( inquiryCount == 0 ))
- {
- // Ask the device for 6 bytes, because most devices will give
- // us 6 anyways ( this way we avoid an overrun error)
- inquiryCount = 6;
- }
-
- err = InquiryBuilder( ourPB, ourPB->buffer, inquiryCount );
- }
- else
- {
- //err = kClassNotConfiguredError;
- // We could not get a request sense, device may not be responding
- // allow shim to remove this driver.
- err = ioErr;
- }
- }
- break;
-
- case kInitGetInquirySizeDoneState:
- {
- if ( err == noErr)
- {
- UInt8 TotalOfInq;
-
- TotalOfInq = 5 + ourPB->buffer[4];
-
- // Check to make sure we are not asking for more
- // data than the buffer can hold.
- if ( TotalOfInq > kInternalPBBufferSize )
- {
- TotalOfInq = kInternalPBBufferSize;
- }
-
- ourPB->currentExecutionState = kInitInquiryDoneState;
- ourPB->completionProc = &InitStateMachine;
- err = InquiryBuilder( ourPB, ourPB->buffer, TotalOfInq );
- }
- else
- {
- err = kClassNotConfiguredError;
- }
- }
- break;
-
- case kInitInquiryDoneState:
- {
- if ( err != noErr)
- {
- err = kClassNotConfiguredError;
- }
- else
- {
- err = ProcessInquiryData( ourPB->buffer );
- }
- }
- break;
- }
-
- if ( err != kRequestPending )
- {
- UInt32 theUserData;
- ControlStatusCompletionProcPtr theCallBack = (ControlStatusCompletionProcPtr) ourPB->userCompletionProc;
-
- theUserData = ourPB->userData;
- FreeInternalPB( ourPB );
- (*theCallBack) ( theUserData, err );
- }
- }
-
- OSStatus ProcessInquiryData( UInt8 *InquiryBuffer )
- {
- UInt8 loopCount;
- UInt8 replySize;
- StandardInquiryDataPtr inqReplyDataPtr;
-
- inqReplyDataPtr = (StandardInquiryDataPtr) InquiryBuffer;
-
- replySize = inqReplyDataPtr->additionalLength + 5;
-
- // Make sure that we only view what is in the buffer
- // as part of the data.
- if ( replySize > kInternalPBBufferSize )
- {
- replySize = kInternalPBBufferSize;
- }
-
- // Check if there is a valid device connected.
- if ( (inqReplyDataPtr->qualifierAndType & kInqPeripheralQualifierMask) != kInqPeripheralConnected )
- {
- // Check the Peripheral Qualifier to determine if there us a connected device at this LUN
- // Since ATAPI does not support LUNs, this will be always be kInqPeripheralConnected for
- // devices with an ATAPI interface.
-
- // If there is not a valid device, return an error and allow ourselves to be removed
- return ioErr;
- }
-
- // Check if the connected device is of a supported device class.
- switch ( inqReplyDataPtr->qualifierAndType & kInqPeripheralDeviceTypeMask )
- {
- case kInqDirectAccessSBCDevice:
- case kInqWriteOnceSBCDevice:
- case kInqCDROM_MMCDevice:
- case kInqOpticalMemorySBCDevice:
- case kInqSimplifiedDirectAccessRBCDevice:
- {
- // The connected device is of a supported device class
- // break and proceed with the processing
- gPeripheralDeviceType = inqReplyDataPtr->qualifierAndType & kInqPeripheralDeviceTypeMask;
- }
- break;
-
- //kInqSequentialAccessSSCDevice = 0x01,
- //kInqPrinterSSCDevice = 0x02,
- //kInqProcessorSPCDevice = 0x03,
- //kInqScannerSCSI2Device = 0x06,
- //kInqMediumChangerSMCDevice = 0x08,
- //kInqCommunicationsSSCDevice = 0x09,
- /* 0x0A - 0x0B ASC IT8 Graphic Arts Prepress Devices */
- //kInqStorageArrayControllerSCC2Device = 0x0C,
- //kInqEnclosureServicesSESDevice = 0x0D,
- //kInqOpticalCardReaderOCRWDevice = 0x0F,
- /* 0x10 - 0x1E Reserved Device Types */
- //kInqUnknownOrNoDeviceType = 0x1F,
- default:
- {
- // The device class is not supported, return an error
- // and allow the driver to be removed.
- return ioErr;
- }
- break;
- }
-
- // This check is because the SanDisk does not return a Full Inquiry
- if ( replySize > 8)
- {
- // If we couldn't get a Vendor name string from the class driver,
- // we need to build one now
- if (gDeviceInfoStruct.vendorName == nil )
- {
- // Bytes 8-15, count 8
- for( loopCount = 0; loopCount < kVendorIDLen; loopCount++)
- {
- gVendorIDString[loopCount+1] = inqReplyDataPtr->vendorID[loopCount];
- }
-
- // Remove any blank spaces at the end of the Vendor
- // name if any exists.
- for( loopCount; loopCount >= 0; loopCount--)
- {
- if (gVendorIDString[loopCount+1] != 0x20)
- {
- break;
- }
- }
-
- gVendorIDString[0] = loopCount;
- gDeviceInfoStruct.vendorName = gVendorIDString;
- }
-
- // If we couldn't get a Product name string from the class driver,
- // use the one we just built
- if (gDeviceInfoStruct.productName == nil )
- {
- // Bytes 16-31, count 16
- // Build Product ID string. We use this to determine if we have an LS-120
- for( loopCount = 0; loopCount < kProductIDLen; loopCount++)
- {
- gProductIDString[loopCount+1] = inqReplyDataPtr->productID[loopCount];
- }
-
- // Remove any blank spaces at the end of the Product
- // name if any exists.
- for( loopCount; loopCount >= 0; loopCount--)
- {
- if (gProductIDString[loopCount+1] != 0x20)
- {
- break;
- }
- }
-
- gProductIDString[0] = loopCount;
- gDeviceInfoStruct.productName = gProductIDString;
- }
-
- // Get the Product Revison number
- // Bytes 32-35 count 4
- gFWRevisionString[0] = kProductRevLen;
- for( loopCount = 0; loopCount < kProductRevLen; loopCount++)
- {
- gFWRevisionString[loopCount+1] = inqReplyDataPtr->productRevision[loopCount];
- }
-
- gDeviceInfoStruct.revisionNumber = gFWRevisionString;
-
- // These use the Inquiry buffer since they are not currently defined
- // in the Inquiry structure.
- if ( inqReplyDataPtr->additionalLength > 31 )
- {
- // Get the subrevision number, Byte 36 count 1
- gFWSubRevisionString[0] = 1;
- gFWSubRevisionString[1] = InquiryBuffer[36];
-
- gDeviceInfoStruct.subRevisionNumber = gFWSubRevisionString;
- }
-
- if ( inqReplyDataPtr->additionalLength == 123 )
- {
- // Get the Serial Number
- gSerialNumberString[0] = 12; // Bytes 116-127, count 12
- for( loopCount = 0; loopCount < 12; loopCount++)
- {
- gSerialNumberString[loopCount+1] = InquiryBuffer[116+loopCount];
- }
-
- gDeviceInfoStruct.serialNumber = gSerialNumberString;
- }
- }
-
- // Check if this is a Removable device
- if ( (inqReplyDataPtr->removableBit & kInqRemovableMask) != 0)
- {
- gDriveIsFixed = false;
-
- // Check if this is the floppy subclass
- if ( gDeviceUSBSubClass == kUSBStorageUFISubclass )
- {
- // If this is a floppy subclass device, we already know it supports floppies
- gDriveSupportsFloppies = true;
-
- // Add floppies to the list of supported media types
- gSupportedMediaTypes.supportedTypesArray[gSupportedMediaTypes.supportTypesCount] = kdgFloppyType;
- gSupportedMediaTypes.supportTypesCount++;
-
- gDriveSupportsNonFloppyRemovable = false;
- }
- else
- {
- // Not a floppy subclass, so must be large capacity removables
-
- // But is it a CD/DVD Drive?
- if (( inqReplyDataPtr->qualifierAndType & kInqPeripheralDeviceTypeMask ) == kInqCDROM_MMCDevice )
- {
- // It is a CD Drive
- gDriveSupportsNonFloppyRemovable = true;
- gDriveSupportsFloppies = false;
-
- gSupportedMediaTypes.supportedTypesArray[gSupportedMediaTypes.supportTypesCount] = kdgCDType;
- gSupportedMediaTypes.supportTypesCount++;
- }
- else
- {
- // Not a CD Drive
- gDriveSupportsNonFloppyRemovable = true;
-
- // Add Removables to the list of supported media types
- gSupportedMediaTypes.supportedTypesArray[gSupportedMediaTypes.supportTypesCount] = kdgRemovableType;
- gSupportedMediaTypes.supportTypesCount++;
-
- // Check if this drive supports floppy disk
- if ( kDriveSupportsFloppyDisk == true)
- {
- // This drive can also handle floppy disks
- gDriveSupportsFloppies = true;
-
- // Add floppies to the list of supported media types
- gSupportedMediaTypes.supportedTypesArray[gSupportedMediaTypes.supportTypesCount] = kdgFloppyType;
- gSupportedMediaTypes.supportTypesCount++;
- }
- else
- {
- // This is removable only device, cannot handle floppy disks
- gDriveSupportsFloppies = false;
- }
- }
- }
- }
- else
- {
- gDriveIsFixed = true;
-
- // Add Fixed Disk to the list of supported media types
- gSupportedMediaTypes.supportedTypesArray[gSupportedMediaTypes.supportTypesCount] = kdgDiskType;
- gSupportedMediaTypes.supportTypesCount++;
- gDriveSupportsNonFloppyRemovable = false;
- }
-
- // Check to determine which version of the commands should
- // be used for the attached device.
- gCommandSetANSIVersion = inqReplyDataPtr->version & kInqANSIVersionMask;
- return noErr;
- }
-
- OSStatus TerminateDriveAccess ( void )
- {
- // Make sure that if there is a pending Manual Eject interrupt, it is cancelled
- CancelManualEjectInterrupt();
-
- // Clear the strings out now that this device is gone
- gDeviceInfoStruct.vendorName = nil;
- gDeviceInfoStruct.productName = nil;
- gDeviceInfoStruct.revisionNumber = nil;
- gDeviceInfoStruct.subRevisionNumber = nil;
- gDeviceInfoStruct.serialNumber = nil;
-
- while( IsCommandPending() == true )
- {
- //SysDebugStr("\pWait until done");
- // Wait for the pending command to finish before
- // proceeding with the terminate
- }
-
- (void) TerminateClassAccess();
-
- return noErr;
- }
-
- #pragma mark -
- #pragma mark Check For Media Routines
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Check For Media
- // All functions and variables needed to check for the presence
- // of media in the attached device.
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- // State Machine States for the Check For Media Machine
- enum
- {
- kCheckStartState = 1,
- kCheckTURDoneState,
- kYEDataCheckDoneState,
- kCheckReqSenseDoneState,
- kCheckZipNonSenseDoneState,
- kCheckFloppyCheckFormatsState
- };
-
- static void CheckMediaMachine ( IntDriveRequestPB *ourPB );
-
- enum
- {
- kGetGeometryStartState = 1,
- kGetGeometryDoneState,
- kGetGeometryErrorDone,
- kGetGeometryPossibleCapacitiesDone
- };
-
- static void GetGeometryStateMachine( IntDriveRequestPBPtr ourPB );
-
- // State Machine States for the Check For Media Machine
- enum
- {
- kWriteProtectStartState = 1,
- kWriteProtectModeSenseDoneState,
- kWriteProtectReqSenseDoneState
- };
-
- static void CheckWriteProtectMachine( IntDriveRequestPB *ourPB );
-
- // External Device Access Functions
- OSStatus CheckIfMediaIsPresent( UInt32 userData, CheckIfMediaIsPresentCompletionProcPtr callBack )
- {
- IntDriveRequestPBPtr ourPB;
-
- if ( IsDeviceAccessEnabled() == false )
- {
- return kDeviceAccessNoMediaError;
- }
-
- if ( gWaitingForEjectCompletion == true )
- {
- // We are currently waiting for manual eject media
- // to be removed, until it is, we won't try to remount.
- return kDeviceAccessNoMediaError;
- }
-
- ourPB = GetDriveInternalPB();
- if ( ourPB == nil )
- {
- return kDeviceAccessNoMediaError;
- }
-
- ourPB->userData = userData;
- ourPB->userCompletionProc = callBack;
- ourPB->currentExecutionState = kCheckStartState;
- ourPB->completionProc = &CheckMediaMachine;
- ourPB->status = noErr;
- CheckMediaMachine( ourPB );
-
- return kRequestPending;
- }
-
- void CheckMediaMachine( IntDriveRequestPB *ourPB )
- {
- OSStatus err = ourPB->status;
-
- switch (ourPB->currentExecutionState )
- {
- case kCheckStartState:
- {
- if (( gDriveIsFixed == true ) && ( gRemountFixed == false ))
- {
- err = kDeviceAccessNoMediaError;
- break;
- }
-
- ourPB->completionProc = &CheckMediaMachine;
- ourPB->currentExecutionState = kCheckTURDoneState;
-
- err = TestUnitReadyBuilder( ourPB );
- }
- break;
-
- case kCheckTURDoneState:
- {
- ourPB->currentExecutionState = kCheckReqSenseDoneState;
- ourPB->completionProc = &CheckMediaMachine;
- err = RequestSenseBuilder( ourPB, (Ptr) ourPB->buffer );
- }
- break;
-
- case kCheckReqSenseDoneState:
- {
- FixRequestSenseData( ourPB );
- err = ourPB->status;
- if (err == noErr)
- {
- // Check the Sense Key to see if it an error group we know how to handle.
- if (((ourPB->buffer[2] & 0x0F) == 0x02) || ((ourPB->buffer[2] & 0x0F) == 0x03))
- {
- // The Sense Key is an 02 (Not Ready) or 03 (Medium Error), check to see if there
- // is something that needs to be done to fix this.
- if ((ourPB->buffer[12] == 0x04) && (ourPB->buffer[13] == 0x02))
- {
- // We need to send a start command, then restart the machine
- ourPB->currentExecutionState = kCheckStartState;
- ourPB->completionProc = &CheckMediaMachine;
- err = StartStopEjectCartridgeBuilder( ourPB, kStartDevice, kDontEjectMedia);
- break;
- }
- else if ((ourPB->buffer[12] == 0x30) && ((ourPB->buffer[13] == 0x00) || (ourPB->buffer[13] == 0x02)))
- {
- // The drive says the media is incompatible, with this device, inform the User and
- // inform UnitTable module.
- ShowUnusableMediaDialog();
- err = kDeviceAccessMediaUnusable;
- break;
- }
- else if (((ourPB->buffer[12] == 0x30) && (ourPB->buffer[13] == 0x01))
- || ((ourPB->buffer[12] == 0x31) && (ourPB->buffer[13] == 0x00)))
- {
- // The device says the media has an unknown or corrupted format.
- // It may needed to be formatted or reformatted. Act like we have
- // good media and let Finder display the please initialize dialog for
- // the user. No need to do anything here, it will be handled below.
- }
- else
- {
- // The drive is not ready. This means that either there is no media in the device,
- // or the device may be spinning the media up. In either case, act like there is
- // no media and the UnitTable module will call use again after a delay.
- err = kDeviceAccessNoMediaError;
- break;
- }
- }
- else if ((ourPB->buffer[2] & 0x0F) > 0x01)
- {
- // If the Sense Key is anything but 00 (No Sense), 01 (Recovered Error) or
- // 03 (Medium Error) it is an error that we cannot handle, return no Media
- err = kDeviceAccessNoMediaError;
- break;
- }
-
- // It appears we have media and the drive is ready, let the UnitTable Module
- // try to mount it.
- if ( WasManualEjectRemoveIssued() == true )
- {
- RemoveCurrentDialog();
- ClearManualEjectRemoveIssued();
- }
-
- if ( gDeviceUSBSubClass == kUSBStorageUFISubclass )
- {
- // If this is the floppy subclass device, check formats
- // capacity data to determine if drive is ready.
- ourPB->currentExecutionState = kCheckFloppyCheckFormatsState;
- ourPB->completionProc = &CheckMediaMachine;
- err = ReadFormatCapacityBuilder( ourPB, ourPB->buffer, 0x0C);
- }
- }
- else
- {
- err = kDeviceAccessNoMediaError;
-
- }
- }
- break;
-
- case kCheckZipNonSenseDoneState:
- {
- RemoveCurrentDialog();
- if (err == noErr)
- {
- if ( ourPB->buffer[21] == 0x05 )
- {
- // This cartridge is Read protected, inform the User and inform UnitTable module
- ShowUnusableMediaDialog();
- err = kDeviceAccessMediaUnusable;
- }
- else
- {
- err = noErr;
- }
- }
- else
- {
- err = kDeviceAccessNoMediaError;
- }
- }
- break;
-
- case kCheckFloppyCheckFormatsState:
- {
- if (( ourPB->buffer[3] == 0x03 ) || (err != noErr))
- {
- // There is currently no media in the drive
- err = kDeviceAccessNoMediaError;
- }
- else
- {
- // There is media in the drive
- err = noErr;
- }
- }
- break;
- }
-
- if ( err != kRequestPending )
- {
-
- #if 0
- if ( err == noErr )
- {
- // We have media in the driver inform the class driver, that it
- // should not be replace.
- UInt8 lockRequest = kLockROMDriver;
- OSStatus status;
-
- status = SendClassControlCall(kUSBStorageControlLockROMDriver, &lockRequest);
- // If the driver can't be locked, should we post an error to keep the media from mounting and to
- // avoid the drivers from being replaced after a PostEvent is made?
- }
- #endif
-
- if ( err == noErr )
- {
- // Media is in the drive and no errors has
- // occurred, check the disk capacity.
- ourPB->status = noErr;
- ourPB->currentExecutionState = kGetGeometryStartState;
- ourPB->completionProc = &GetGeometryStateMachine;
- GetGeometryStateMachine( ourPB );
- }
- else
- {
- // An error occurred, no reason to proceed to the check capacity routine.
- UInt32 theUserData;
- CheckIfMediaIsPresentCompletionProcPtr theCallBack = (CheckIfMediaIsPresentCompletionProcPtr) ourPB->userCompletionProc;
-
- theUserData = ourPB->userData;
- FreeInternalPB( ourPB );
- (*theCallBack) ( theUserData, err);
- }
- }
- }
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Get Media Capacity
- // All functions and variables needed to handle the determination
- // of the capacity of the media in the attached device.
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- void GetGeometryStateMachine( IntDriveRequestPBPtr ourPB )
- {
- OSStatus err = ourPB->status;
-
- switch (ourPB->currentExecutionState)
- {
- case kGetGeometryStartState:
- {
- gCurrentMediaCapacity.TotalBlocksOnMedia = 0;
- gCurrentMediaCapacity.BlockLengthInBytes = 0;
- ourPB->currentExecutionState = kGetGeometryDoneState;
- ourPB->completionProc = &GetGeometryStateMachine;
- if ( gDeviceUSBSubClass == kUSBStorageUFISubclass )
- {
- // We have a floppy drive, do a Read Format capacitities
- err = ReadFormatCapacityBuilder( ourPB, ourPB->buffer, 0x0C);
- }
- else
- {
- err = GetMediaGeometryBuilder( ourPB, &gCurrentMediaCapacity );
- }
- }
- break;
-
- case kGetGeometryDoneState:
- {
- if (gDeviceUSBSubClass == kUSBStorageUFISubclass )
- {
- gCurrentMediaCapacity.TotalBlocksOnMedia = *((UInt32 *) &ourPB->buffer[4]);
- gCurrentMediaCapacity.BlockLengthInBytes = *((UInt16 *) &ourPB->buffer[10]);
- }
- else
- {
- // Need to add one because GetCapacity command returns last block number, not number of blocks
- gCurrentMediaCapacity.TotalBlocksOnMedia +=1;
- }
-
- if((gCurrentMediaCapacity.TotalBlocksOnMedia == 0) || (gCurrentMediaCapacity.BlockLengthInBytes == 0))
- {
- //Find out why the capacity is zero
- ourPB->currentExecutionState = kGetGeometryErrorDone;
- ourPB->completionProc = &GetGeometryStateMachine;
- err = RequestSenseBuilder( ourPB, (Ptr) ourPB->buffer );
- }
- else
- {
- err = noErr;
- }
- }
- break;
-
- case kGetGeometryErrorDone:
- {
- if( (ourPB->buffer[12] == 0x30) && (ourPB->buffer[13] == 0x01))
- {
- // We have an unformatted cartridge, find out what format it can have
- ourPB->currentExecutionState = kGetGeometryPossibleCapacitiesDone;
- ourPB->completionProc = &GetGeometryStateMachine;
- err = ReadFormatCapacityBuilder(ourPB, ourPB->buffer, 0x0C);
- }
- else
- {
- err = kDeviceAccessNoMediaError;
- }
- }
- break;
-
- case kGetGeometryPossibleCapacitiesDone:
- {
- FixRequestSenseData( ourPB );
- err = ourPB->status;
- if( (ourPB->buffer[3] >= 0x08) && (ourPB->buffer[8] & 0x01 == 0x01))
- {
- // this is the maximum format for this cartridge
- gCurrentMediaCapacity.TotalBlocksOnMedia = *((UInt32 *) &ourPB->buffer[4]);
- gCurrentMediaCapacity.BlockLengthInBytes = *((UInt16 *) &ourPB->buffer[10]);
-
- err = noErr;
- }
- else
- {
- err = kDeviceAccessNoMediaError;
- }
- }
- break;
- }
-
- if ( err != kRequestPending )
- {
- if ( err == noErr )
- {
- // Media is in the drive, disk capacity was checked and,
- // so far, no errors have occurred, check if media is write protected.
- ourPB->currentExecutionState = kWriteProtectStartState;
- ourPB->completionProc = &CheckWriteProtectMachine;
- ourPB->status = noErr;
- CheckWriteProtectMachine( ourPB );
- }
- else
- {
- // An error occurred, no reason to proceed to the check write protect routine.
- UInt32 theUserData;
- CheckIfMediaIsPresentCompletionProcPtr theCallBack = (CheckIfMediaIsPresentCompletionProcPtr) ourPB->userCompletionProc;
-
- theUserData = ourPB->userData;
- FreeInternalPB( ourPB );
- (*theCallBack) ( theUserData, err);
- }
- }
- }
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Check For Write Protect
- // All functions and variables needed to check whether the
- // media in the attached device is write protected.
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- void CheckWriteProtectMachine( IntDriveRequestPB *ourPB )
- {
- OSStatus err = ourPB->status;
-
- switch (ourPB->currentExecutionState )
- {
- case kWriteProtectStartState:
- {
- // Check to see if media is write protected
- ourPB->currentExecutionState = kWriteProtectModeSenseDoneState;
- ourPB->completionProc = &CheckWriteProtectMachine;
-
- if ( gPeripheralDeviceType == kInqWriteOnceSBCDevice )
- {
- // This is a write once type device. In order to be safe and not waste
- // space on the cartridge, the driver will report this media as Write Protected.
- gMediaIsWriteProtected = true;
- err = noErr;
- }
- else if ( gPeripheralDeviceType == kInqCDROM_MMCDevice )
- {
- // This is a CD or DVD drive, should determine depending
- // on media type. For now, it is always write protected.
- gMediaIsWriteProtected = true;
- err = noErr;
- }
- else if ( gPeripheralDeviceType == kInqSimplifiedDirectAccessRBCDevice )
- {
- // We have a RBC SCSI device, use the 6 byte version of the command and
- // check the RBC Device Parameter page.
- err = ModeSenseBuilder(ourPB, kRBCCommand, ourPB->buffer, kRBCDeviceParametersModePageCode,
- kMode6ParameterHeaderLength+kRBCDeviceParametersModePageLength);
- }
- else if ( gCommandSetANSIVersion > 0 )
- {
- // We have a non MMC SCSI device, use the 6 byte version of the command
- err = ModeSenseBuilder(ourPB, kRBCCommand, ourPB->buffer, kAllModePages, kMode6ParameterHeaderLength);
- }
- else
- {
- // This is a non MMC ATAPI device, use the 10 byte version.
- err = ModeSenseBuilder(ourPB, kSPCCommand, ourPB->buffer, kAllModePages, kMode10ParameterHeaderLength);
- }
- }
- break;
-
- case kWriteProtectModeSenseDoneState:
- {
- if ( err == noErr )
- {
- if ( gPeripheralDeviceType == kInqSimplifiedDirectAccessRBCDevice )
- {
- // This is an RBC device, check the RBC Device Parameter mode page.
- // Check byte 11, bit 2 in the page descriptor.
- if (( ourPB->buffer[11 + kMode6ParameterHeaderLength] & 0x04 ) != 0 )
- {
- gMediaIsWriteProtected = true;
- err = noErr;
- }
- else
- {
- gMediaIsWriteProtected = false;
- err = noErr;
- }
- }
- else if ((( ourPB->executePB.cdb[0] == kCmdModeSense6 ) && (( ourPB->buffer[2] & 0x80 ) != 0 ))
- || (( ourPB->executePB.cdb[0] == kCmdModeSense10 ) && (( ourPB->buffer[3] & 0x80 ) != 0 )))
- {
- gMediaIsWriteProtected = true;
- err = noErr;
- }
- else
- {
- gMediaIsWriteProtected = false;
- err = noErr;
- }
- }
- else
- {
- // Since an error occurred while trying to determine if the media is
- // write protected, say that it is just to be on the safe side.
- gMediaIsWriteProtected = true;
- ourPB->currentExecutionState = kWriteProtectReqSenseDoneState;
- ourPB->completionProc = &CheckWriteProtectMachine;
- err = RequestSenseBuilder( ourPB, (Ptr) ourPB->buffer );
- }
- }
- break;
-
- case kWriteProtectReqSenseDoneState:
- {
- err = noErr;
- }
- break;
- }
-
- if ( err != kRequestPending )
- {
- UInt32 theUserData;
- CheckIfMediaIsPresentCompletionProcPtr theCallBack = (CheckIfMediaIsPresentCompletionProcPtr) ourPB->userCompletionProc;
-
- theUserData = ourPB->userData;
- FreeInternalPB( ourPB );
- (*theCallBack) (theUserData, err);
- }
- }
-
- #pragma mark -
- #pragma mark Eject State Machine
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Eject the Media
- // All functions and variables needed to handle the ejection
- // of media in the attached device.
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Eject State machine states
- enum
- {
- kEjectProcessStartState = 1,
- kEjectCommandDoneState,
- kEjectReqSenseDoneState,
- kManEjectStartState,
- kManEjectTURDoneState,
- kManEjectReqSenseDoneState
- };
-
- static void HandleEjectStateMachine( IntDriveRequestPBPtr ourPB );
- static OSStatus EjectCheckInterrupt( void *p1, void *p2);
- static TimerID EjectCheckInterruptTimer = 0;
-
- OSStatus EjectCartridge( UInt32 userData, EjectCartridgeCompletionProcPtr callBack )
- {
- #pragma unused ( userData, callBack )
- IntDriveRequestPBPtr ourPB;
-
- ClearManualEjectInterrupt();
-
- gCurrentMediaCapacity.TotalBlocksOnMedia = 0;
- gCurrentMediaCapacity.BlockLengthInBytes = 0;
-
- if ( gDriveIsFixed == true )
- {
- gRemountFixed = false;
-
- // Make the system think that we ejected the disk
- return noErr;
- }
-
- if ( IsDeviceAccessEnabled() == false )
- {
- // Make the system think that we ejected the disk
- return noErr;
- }
-
- if ( WasManualEjectRemoveIssued() == true )
- {
- // The disk was already removed, no need to try eject.
- ClearManualEjectRemoveIssued();
- return noErr;
- }
-
- gWaitingForEjectCompletion = true;
- ourPB = GetDriveInternalPB();
-
- // Since we will immediately call back, no need to save this information
- /* ourPB->userData = userData;*/
- /* ourPB->userCompletionProc = callBack;*/
-
- ourPB->currentExecutionState = kEjectProcessStartState;
- if(( IsDeviceKnownManualEject() == true ) || ( gPreventCommandFailed == true ))
- {
- // This device is manual eject, Display dialog and wait for user to remove disk
- ShowPleaseEjectMediaDialog();
- ourPB->currentExecutionState = kManEjectStartState;
- }
- else
- {
- ourPB->currentExecutionState = kEjectProcessStartState;
- }
-
- ourPB->completionProc = &HandleEjectStateMachine;
- HandleEjectStateMachine( ourPB );
-
- return noErr;
- }
-
- OSStatus EjectCheckInterrupt( void *p1, void *p2)
- {
- #pragma unused ( p2 )
- IntDriveRequestPBPtr ourPB;
-
- EjectCheckInterruptTimer = 0;
- ourPB = (IntDriveRequestPBPtr) p1;
- ourPB->currentExecutionState = kManEjectStartState;
- ourPB->status = noErr;
- HandleEjectStateMachine( ourPB );
-
- return noErr;
- }
-
-
- void HandleEjectStateMachine( IntDriveRequestPBPtr ourPB )
- {
- OSStatus err = ourPB->status;
- Boolean resetTheInterrupt = false;
-
- switch (ourPB->currentExecutionState)
- {
- case kEjectProcessStartState:
- {
- ourPB->currentExecutionState = kEjectCommandDoneState;
- ourPB->completionProc = &HandleEjectStateMachine;
- err = StartStopEjectCartridgeBuilder( ourPB, kStopDevice, kEjectMedia);
- }
- break;
-
- case kEjectCommandDoneState:
- {
- if ( ourPB->autoStatusIsValid == true )
- {
- // We have a device that reports the sense in the interrupt, don't need to do Req Sense
- if (((ourPB->autoStatus[0] == 0x24) && (ourPB->status == noErr)) || ( IsDeviceKnownManualEject() == true ) || ( gPreventCommandFailed == true))
- {
- // Don't make the caller wait for the media to be removed.
- /* if ( ourPB->userCompletionProc != nil )*/
- /* {*/
- /* (*(EjectCartridgeCompletionProcPtr) ourPB->userCompletionProc) ( ourPB->userData, err );*/
- /* ourPB->userCompletionProc = nil;*/
- /* ourPB->userData = 0;*/
- /* }*/
-
- ShowPleaseEjectMediaDialog();
- ourPB->currentExecutionState = kManEjectTURDoneState;
- ourPB->completionProc = &HandleEjectStateMachine;
- err = TestUnitReadyBuilder( ourPB );
- }
- else
- {
- // The cartridge was ejected
- err = noErr;
- }
- }
- else
- {
- // We need issue a request sense to get sense data
- ourPB->currentExecutionState = kEjectReqSenseDoneState;
- ourPB->completionProc = &HandleEjectStateMachine;
- err = RequestSenseBuilder( ourPB, (Ptr) ourPB->buffer );
- }
- }
- break;
-
- case kEjectReqSenseDoneState:
- {
- FixRequestSenseData( ourPB );
- err = ourPB->status;
- if(((ourPB->buffer[12] == 0x24) && ( err == noErr )) || ( IsDeviceKnownManualEject() == true ) || ( gPreventCommandFailed == true ))
- {
- // This device is manual eject, Display dialog and wait for user to remove disk
- /* if ( ourPB->userCompletionProc != nil )*/
- /* {*/
- /* (*(EjectCartridgeCompletionProcPtr) ourPB->userCompletionProc) ( ourPB->userData, err );*/
- /* ourPB->userCompletionProc = nil;*/
- /* ourPB->userData = 0;*/
- /* }*/
- ShowPleaseEjectMediaDialog();
- ourPB->currentExecutionState = kManEjectTURDoneState;
- ourPB->completionProc = &HandleEjectStateMachine;
-
- err = TestUnitReadyBuilder( ourPB );
-
- }
- else
- {
- // The cartridge was ejected
- err = noErr;
- }
- }
- break;
-
- case kManEjectStartState:
- {
- ourPB->currentExecutionState = kManEjectTURDoneState;
- ourPB->completionProc = &HandleEjectStateMachine;
- err = TestUnitReadyBuilder( ourPB );
-
- }
- break;
-
- case kManEjectTURDoneState:
- {
- if ( ourPB->autoStatusIsValid == true )
- {
- // We have a device that reports the sense in the interrupt, don't need to do Req Sense
- if ((ourPB->autoStatus[0] != 0x00) && (ourPB->status == noErr))
- {
- RemoveCurrentDialog();
- err = noErr;
- }
- else
- {
- resetTheInterrupt = true;
- }
- }
- else
- {
- // We need issue a request sense to get sense data
- ourPB->currentExecutionState = kManEjectReqSenseDoneState;
- ourPB->completionProc = &HandleEjectStateMachine;
- err = RequestSenseBuilder( ourPB, (Ptr) ourPB->buffer );
- }
- }
- break;
-
- case kManEjectReqSenseDoneState:
- {
- FixRequestSenseData( ourPB );
- err = ourPB->status;
- if(((ourPB->buffer[12] == 0x28) || (ourPB->buffer[12] == 0x3A)) && ( err == noErr))
- {
- // The drive returned sense data and indicates that either there is media currently
- // in the drive, or the media in the drive has been changed since the last Request
- // Sense command was issued. In either case, remove the dialog and leave this state
- // machine.
- RemoveCurrentDialog();
- err = noErr;
- }
- else
- {
- resetTheInterrupt = true;
- }
- }
- break;
- }
-
- if ( err != kRequestPending )
- {
- gPreventCommandFailed = false;
- gWaitingForEjectCompletion = false;
-
- if ( resetTheInterrupt == true )
- {
- AbsoluteTime theWait;
-
- theWait = DurationToAbsolute( durationSecond );
- theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
- SetInterruptTimer( &theWait, &EjectCheckInterrupt, ourPB, &EjectCheckInterruptTimer);
- }
- else
- {
- // We have finished, free the PB, so it can be used by others.
- FreeInternalPB( ourPB );
- }
- }
- }
-
- #pragma mark -
-
- static void PreventRequestCompletion( IntDriveRequestPBPtr requestPB );
-
- OSStatus PreventMediaRemoval( UInt32 userData, LockMediaCompletionProcPtr callBack )
- {
- IntDriveRequestPBPtr ourPB;
- OSStatus err = noErr;
-
- ourPB = GetDriveInternalPB();
-
- ourPB->userData = userData;
- ourPB->userCompletionProc = callBack;
-
- ourPB->completionProc = &PreventRequestCompletion;
-
- // We already know this is manual eject, or we know that it definitely is
- // not manual ejectable.
- if (( IsDeviceKnownManualEject() == true ) || ( gDriveIsFixed == true ))
- {
- err = TestUnitReadyBuilder( ourPB );
- }
- else
- {
- err = PreventAllowRemovalBuilder( ourPB, kPreventMediaRemoval );
- }
-
- if ( err != kRequestPending )
- {
- FreeInternalPB( ourPB );
- }
-
- return err;
- }
-
- void PreventRequestCompletion( IntDriveRequestPBPtr requestPB )
- {
- // If the Prevent command was illegal, we have a manual eject disk
- // set the interrupt up to start
- OSStatus theStatus = requestPB->status;
- LockMediaCompletionProcPtr theCallBack = (LockMediaCompletionProcPtr) requestPB->userCompletionProc;
- UInt32 theUserData = requestPB->userData;
-
- //Set the polling interrupt
- if((( theStatus != noErr ) || (IsDeviceKnownManualEject() == true)) && ( gDriveIsFixed == false ))
- {
- gPreventCommandFailed = true;
- SetManualCheckInterrupt();
- }
- else
- {
- gPreventCommandFailed = false;
- }
-
- FreeInternalPB( requestPB );
- (*theCallBack) ( theUserData, theStatus );
- }
-
- OSStatus AllowMediaRemoval( UInt32 userData, UnlockMediaCompletionProcPtr callBack )
- {
- OSStatus err = noErr;
-
- if (( IsDeviceKnownManualEject() == true ) || ( gPreventCommandFailed == true ))
- {
- // Media is not locked in drive so no allow media
- // removal command is needed. Return an immediate noErr.
- err = noErr;
- }
- else
- {
- // The media was successfully locked into the drive so an allow
- // media removal command is needed before an eject can be done.
- IntDriveRequestPBPtr ourPB;
-
- ourPB = GetDriveInternalPB();
-
- ourPB->userData = userData;
- ourPB->userCompletionProc = callBack;
- ourPB->completionProc = &ExternalRequestComplete;
-
- err = PreventAllowRemovalBuilder( ourPB, kAllowMediaRemoval );
- if ( err != kRequestPending )
- {
- FreeInternalPB( ourPB );
- }
- }
-
- return err;
- }
-
- void ExternalRequestComplete ( IntDriveRequestPBPtr requestPB )
- {
- DefaultDAMCompletionProcPtr theCallBack = (DefaultDAMCompletionProcPtr) requestPB->userCompletionProc;
- OSStatus theStatus = requestPB->status;
- UInt32 theUserData = requestPB->userData;
-
- FreeInternalPB( requestPB );
- (*theCallBack) ( theUserData, theStatus );
- }
-
- void ReadWriteRequestComplete ( IntDriveRequestPBPtr requestPB )
- {
- OSStatus theStatus = requestPB->status;
-
- if ( requestPB->status == noErr )
- {
- if (requestPB->isWriteRequest == true )
- {
- // This was a write command. Make sure that no error occurred
- // by doing a request sense on the device.
- requestPB->completionProc = &WriteRequestSenseComplete;
-
- theStatus = RequestSenseBuilder( requestPB, (Ptr) requestPB->buffer );
- }
- else
- {
- // This was a read request and no error occurred,
- // call the client's completion with a good status
- theStatus = noErr;
- }
- }
- else
- {
- // An error occurred, get the request sense to make sure that the device
- // is still in a usuable state. (Some USB devices require a RequestSense to clear
- // internal error conditions.
- requestPB->completionProc = &ReadWriteRequestSenseOnErrorComplete;
-
- theStatus = RequestSenseBuilder( requestPB, (Ptr) requestPB->buffer );
- }
-
- if ( theStatus != kRequestPending )
- {
- DefaultDAMCompletionProcPtr theCallBack = (DefaultDAMCompletionProcPtr) requestPB->userCompletionProc;
- UInt32 theUserData = requestPB->userData;
-
- // Since the status is not kRequestPending, this request must be finished.
- // Transfer back the status to the client.
- FreeInternalPB( requestPB );
- (*theCallBack) ( theUserData, theStatus );
- }
- }
-
- void WriteRequestSenseComplete ( IntDriveRequestPBPtr requestPB )
- {
- DefaultDAMCompletionProcPtr theCallBack = (DefaultDAMCompletionProcPtr) requestPB->userCompletionProc;
- OSStatus returnStatus = ioErr;
- UInt32 theUserData = requestPB->userData;
-
- FixRequestSenseData( requestPB );
- if ( requestPB->status == noErr )
- {
- // If no error occurred on the request sense, check the sense to see
- // if an error occurred on the original request.
- if (( requestPB->buffer[2] & 0x0F !=0 ) && ( requestPB->buffer[2] & 0x0F !=1 ))
- {
- // An error has been reported back in the sense key, return
- // an ioErr to the system.
- returnStatus = ioErr;
-
- }
- else
- {
- // No errors has been reported back in the sense key, return
- // a noErr to the system.
- returnStatus = noErr;
- }
- }
- else
- {
- // An error occurred on the RequestSense command. This could
- // indicate a drive problem. Return an error and let the UnitTable Module retry.
- returnStatus = ioErr;
- }
-
- FreeInternalPB( requestPB );
- (*theCallBack) ( theUserData, returnStatus );
- }
-
- void ReadWriteRequestSenseOnErrorComplete ( IntDriveRequestPBPtr requestPB )
- {
- DefaultDAMCompletionProcPtr theCallBack = (DefaultDAMCompletionProcPtr) requestPB->userCompletionProc;
- OSStatus theStatus = ioErr;
- UInt32 theUserData = requestPB->userData;
-
- // An error occurred on the original command and the RequestSense was done to
- // clear the error condition on the drive.
- // Since the original command completed with an error, return an ioErr.
- // Code to check the Sense data should be added to tell the UnitTable Module
- // if the command should be retried.
- FreeInternalPB( requestPB );
- (*theCallBack) ( theUserData, theStatus );
- }
-
- //------------------------------------------------------------------------------
- // Function: ReadWriteSingleBuffer
- //
- // Description: Low level read/write block on the media with retries.
- //
- // Input: drive: Pointer to physical drive record
- // blockAddr: Starting block address
- // blockCount: Number of blocks to read/write
- // buffer: Pointer to buffer
- // doWrite: 1 = write, 0 = read
- //
- // Output: true if successful, false if not
- //-------------------------------------------------------------------------------
- OSStatus ReadWriteSingleBuffer( UInt32 userData, UInt32 startBlock, UInt32 blockCount, Ptr buffer,
- UInt32 byteCount, Boolean doWrite, RWBlocksCompletionProcPtr callBack )
- {
- OSStatus status = noErr;
- StorageExecuteCommandPB *theReadWriteRequest;
- IntDriveRequestPBPtr ourPB;
-
- if ( IsDeviceAccessEnabled() == false )
- {
- // The new error code for MacOS 9.0 and later.
- // It lets the file system know that the device was unplugged.
- return driverHardwareGoneErr;
- }
-
- if ( WasManualEjectRemoveIssued() == true )
- {
- // The disk was removed, do not try to read or write to it.
- return ioErr;
- }
-
- ourPB = GetDriveInternalPB();
-
- ourPB->userData = userData;
- ourPB->userCompletionProc = callBack;
- ourPB->isWriteRequest = doWrite;
- ourPB->completionProc = &ReadWriteRequestComplete;
-
- theReadWriteRequest = &ourPB->executePB;
-
- theReadWriteRequest->userBuffer = buffer; // -> Pointer to user buffer
- theReadWriteRequest->expectedCount = byteCount; // -> Expected number of bytes to transfer
-
- theReadWriteRequest->completionProc = &DeviceRequestCompletion; // -> Completion routine
- theReadWriteRequest->actualCount = 0; // <- Actual number of bytes transferred
- theReadWriteRequest->status = 0; // <- Result of operation
-
- if(doWrite)
- {
- theReadWriteRequest->cdb[0] = kCmdWrite;
- theReadWriteRequest->flags = kStorageDataOut; // -> Expect a data out transfer
-
- if ( gDeviceUSBSubClass == kUSBStorageUFISubclass )
- {
- // We have a floppy device, make sure we wait for the interrupt
- theReadWriteRequest->flags |= kStorageUseCommandCompletionInt; // -> Wait for the interrupt
- }
- }
- else
- {
- theReadWriteRequest->cdb[0] = kCmdRead;
- theReadWriteRequest->flags = kStorageDataIn; // -> Expect a data in transfer
- }
-
- // Set the starting block in the CDB
- theReadWriteRequest->cdb[2] = (startBlock >> 24) & 0xff;
- theReadWriteRequest->cdb[3] = (startBlock >> 16) & 0xff;
- theReadWriteRequest->cdb[4] = (startBlock >> 8) & 0xff;
- theReadWriteRequest->cdb[5] = startBlock & 0xff;
-
- // Set the Block Count in the CDB
- theReadWriteRequest->cdb[7] = (blockCount >> 8) & 0xff;
- theReadWriteRequest->cdb[8] = blockCount & 0xff;
-
- status = SendDeviceRequest( theReadWriteRequest );
- if (status != kRequestPending )
- {
- FreeInternalPB( ourPB );
- }
-
- return status;
- }
-
- //------------------------------------------------------------------------------
- // Function: ReadWriteScatterGatherList
- //
- // Description: Low level read/write block on the media with retries.
- //
- // Input: drive: Pointer to physical drive record
- // blockAddr: Starting block address
- // blockCount: Number of blocks to read/write
- // buffer: Pointer to buffer
- // doWrite: 1 = write, 0 = read
- //
- // Output: true if successful, false if not
- //-------------------------------------------------------------------------------
- OSStatus ReadWriteScatterGatherList( UInt32 userData, UInt32 startBlock, UInt32 blockCount, SGListPtr sgList,
- UInt32 byteCount, Boolean doWrite, RWScatterGatherCompletionProcPtr callBack )
- {
- OSStatus status = noErr;
- StorageExecuteCommandPB *theReadWriteRequest;
- IntDriveRequestPBPtr ourPB;
-
- if ( IsDeviceAccessEnabled() == false )
- {
- // The new error code for MacOS 9.0 and later.
- // It lets the file system know that the device was unplugged.
- return driverHardwareGoneErr;
- }
-
- if ( WasManualEjectRemoveIssued() == true )
- {
- // The disk was removed, do not try to read or write to it.
- return ioErr;
- }
-
- ourPB = GetDriveInternalPB();
-
- ourPB->userData = userData;
- ourPB->userCompletionProc = callBack;
- ourPB->isWriteRequest = doWrite;
- ourPB->completionProc = &ReadWriteRequestComplete;
-
- theReadWriteRequest = &ourPB->executePB;
-
- theReadWriteRequest->userBuffer = (Ptr) sgList; // -> Pointer to user buffer
- theReadWriteRequest->expectedCount = byteCount; // -> Expected number of bytes to transfer
-
- theReadWriteRequest->completionProc = &DeviceRequestCompletion; // -> Completion routine
- theReadWriteRequest->actualCount = 0; // <- Actual number of bytes transferred
- theReadWriteRequest->status = 0; // <- Result of operation
-
- if(doWrite)
- {
- theReadWriteRequest->cdb[0] = kCmdWrite;
- theReadWriteRequest->flags = kStorageDataOut; // -> Expect a data out transfer
-
- if ( gDeviceUSBSubClass == kUSBStorageUFISubclass )
- {
- // We have a floppy disk, make sure we wait for the interrupt
- theReadWriteRequest->flags |= kStorageUseCommandCompletionInt; // -> Wait for the interrupt
- }
- }
- else
- {
- theReadWriteRequest->cdb[0] = kCmdRead;
- theReadWriteRequest->flags = kStorageDataIn; // -> Expect a data in transfer
- }
-
- theReadWriteRequest->flags |= kStorageSGBuffer; // -> Tell class driver that this is a SG list operation
-
- // Set the starting block in the CDB
- theReadWriteRequest->cdb[2] = (startBlock >> 24) & 0xff;
- theReadWriteRequest->cdb[3] = (startBlock >> 16) & 0xff;
- theReadWriteRequest->cdb[4] = (startBlock >> 8) & 0xff;
- theReadWriteRequest->cdb[5] = startBlock & 0xff;
-
- // Set the Block Count in the CDB
- theReadWriteRequest->cdb[7] = (blockCount >> 8) & 0xff;
- theReadWriteRequest->cdb[8] = blockCount & 0xff;
-
- status = SendDeviceRequest( theReadWriteRequest );
- if (status != kRequestPending )
- {
- FreeInternalPB( ourPB );
- }
-
- return status;
- }
-
-
- //------------------------------------------------------------------------------
- // Function: FlushDriveWriteCache
- //
- // Description: Flushes the drive's write cache
- //
- // Input: None.
- //
- // Output: None.
- //-------------------------------------------------------------------------------
- OSStatus FlushDriveWriteCache( UInt32 userData, FlushCacheCompletionProcPtr callBack )
- {
- #pragma unused( userData, callBack )
- // Unfortunately, there is no consistent method for flushing the write cache on
- // all drives. However, all drives should flush the cache within a couple of
- // rotations so we can simply hang out for a bit and hope the cache is flushed.
- // Use a non-interrupt timer since interrupts may be disabled at this time.
- AbsoluteTime theWait;
-
- theWait = DurationToAbsolute(durationMillisecond*500);
- DelayForHardware( theWait ); // delay for max seek and a few rotations (500 msec.)
-
- return noErr;
- }
-
-
- #pragma mark -
- #pragma mark Format Media Support
-
- enum
- {
- kFormatRequestSenseRetryCount = 5,
-
- // Format State Machine
- kFormatStartState = 1,
- kFormatDoneState,
- kFormatRequestSenseDoneState,
- kFormatGetGeometryDoneState
- };
-
- static UInt16 gFormatRetryCount;
-
- static void FormatMediaComplete ( IntDriveRequestPBPtr ourPB );
-
- // Floppy drive track by track format state machine
- static OSStatus FormatFloppyCartridge( IntDriveRequestPBPtr requestPB, UInt32 FormatCapacity, UInt16 blockSize );
-
- // Expects a buffer of at least 0x0C (12)
- OSStatus FormatMedia( UInt32 userData, UInt32 FormatCapacity, UInt16 blockSize, FormatMediaCompletionProcPtr callBack )
- {
- OSStatus status = noErr;
- StorageExecuteCommandPB *commandPB;
- IntDriveRequestPBPtr ourPB;
-
- if ( WasManualEjectRemoveIssued() == true )
- {
- // The disk was removed, do not try to format it.
- return ioErr;
- }
-
- ourPB = GetDriveInternalPB();
-
- ourPB->userData = userData;
- ourPB->userCompletionProc = callBack;
- ourPB->completionProc = &FormatMediaComplete;
- ourPB->currentExecutionState = kFormatDoneState;
-
- gFormatRetryCount = 0;
-
- if ( gDeviceUSBSubClass == kUSBStorageUFISubclass )
- {
- // If this is a floppy subclass device, do a track by track format
- status = FormatFloppyCartridge( ourPB, FormatCapacity, blockSize );
- return status;
- }
-
- commandPB = &ourPB->executePB;
-
- BlockZero( ourPB->buffer, 0x0C);
-
- // Set up the data going out
- // First set up the Defect List Header
- ourPB->buffer[0] = 0;
- ourPB->buffer[1] = 0;
- ourPB->buffer[2] = 0;
- ourPB->buffer[3] = 0x08;
-
- *((UInt32 *) &ourPB->buffer[4]) = FormatCapacity;
- *((UInt16 *) &ourPB->buffer[10]) = blockSize;
-
- commandPB->cdb[0] = kCmdFormat;
- commandPB->cdb[1] = 0x17;
-
- commandPB->flags = kStorageDataOut; // -> Expect a data out transfer
- commandPB->userBuffer = (Ptr) ourPB->buffer; // -> Pointer to user buffer
- commandPB->expectedCount = 0x0C; // -> Expected number of bytes to transfer
-
- commandPB->completionProc = &DeviceRequestCompletion; // -> Completion routine
-
- status = SendDeviceRequest(commandPB);
- if (status != kRequestPending )
- {
- FreeInternalPB( ourPB );
- }
-
- return status;
- }
-
- void FormatMediaComplete ( IntDriveRequestPBPtr ourPB )
- {
- OSStatus err = ourPB->status;
-
- switch (ourPB->currentExecutionState)
- {
- case kFormatDoneState:
- {
- ourPB->currentExecutionState = kFormatRequestSenseDoneState;
- err = RequestSenseBuilder( ourPB, (Ptr) ourPB->buffer );
- }
- break;
-
- case kFormatRequestSenseDoneState:
- {
- FixRequestSenseData( ourPB );
- err = ourPB->status;
- if(err == noErr)
- {
- if(ourPB->buffer[12] == 0x00)
- {
- ourPB->currentExecutionState = kFormatGetGeometryDoneState;
- ourPB->completionProc = &FormatMediaComplete;
- err = GetMediaGeometryBuilder( ourPB, &gCurrentMediaCapacity );
- }
- else
- {
- // The format has failed, inform the OS
- err = ioErr;
- }
- }
- else
- {
- if (gFormatRetryCount < kFormatRequestSenseRetryCount)
- {
- ourPB->currentExecutionState = kFormatRequestSenseDoneState;
- gFormatRetryCount += 1;
- ourPB->completionProc = &FormatMediaComplete;
- err = RequestSenseBuilder( ourPB, (Ptr) ourPB->buffer );
- }
- else
- {
- // The format has failed, inform the OS
- gFormatRetryCount = 0;
- err = ioErr;
- }
- }
- }
- break;
-
- case kFormatGetGeometryDoneState:
- {
- if ( err != noErr )
- {
- err = ioErr;
- gCurrentMediaCapacity.TotalBlocksOnMedia = 0;
- gCurrentMediaCapacity.BlockLengthInBytes = 0;
- }
- else
- {
- if ( gCurrentMediaCapacity.TotalBlocksOnMedia != 0)
- {
- // Since the Read Capacity command returns the last
- // block number, add one to make the actual total.
- gCurrentMediaCapacity.TotalBlocksOnMedia +=1;
- }
- }
- }
- break;
- }
-
- if ( err != kRequestPending )
- {
- UInt32 theUserData;
- FormatMediaCompletionProcPtr theCallBack;
-
- theUserData = ourPB->userData;
- theCallBack = (FormatMediaCompletionProcPtr) ourPB->userCompletionProc;
- FreeInternalPB( ourPB );
- (*theCallBack) ( theUserData, err);
- }
- }
-
- #pragma mark -
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Floppy Format Machine
- // All functions and variables needed to handle the formatting
- // of the media in a floppy only drive.
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- enum
- {
- kFormatFloppyStartState = 1,
- kFormatFloppyFormatTrackSide0,
- kFormatFloppyFormatTrackSide1
- };
-
- static void FormatFloppyStateMachine( IntDriveRequestPBPtr ourPB );
-
- // This is the current track counter
- static UInt16 gCurrentFloppyFormatTrack = 0;
- static UInt32 gFloppyFormatCapacityParam;
- static UInt32 gFloppyFormatBlockSizeParam;
-
- OSStatus FormatFloppyCartridge( IntDriveRequestPBPtr requestPB, UInt32 FormatCapacity, UInt16 blockSize )
- {
- if ( IsDeviceAccessEnabled() == false )
- {
- return kDeviceAccessNoMediaError;
- }
-
- gCurrentFloppyFormatTrack = 0;
- gFloppyFormatCapacityParam = FormatCapacity;
- gFloppyFormatBlockSizeParam = blockSize;
-
- requestPB->currentExecutionState = kFormatFloppyStartState;
- requestPB->completionProc = &FormatFloppyStateMachine;
- requestPB->status = kRequestPending;
- FormatFloppyStateMachine( requestPB );
-
- return kRequestPending;
- }
-
- void FormatFloppyStateMachine( IntDriveRequestPBPtr ourPB )
- {
- OSStatus err = ourPB->status;
-
- switch (ourPB->currentExecutionState)
- {
- case kFormatFloppyStartState:
- {
- // Format current track on side 0
- ourPB->currentExecutionState = kFormatFloppyFormatTrackSide1;
- ourPB->completionProc = &FormatFloppyStateMachine;
- err = FormatFloppyTrackBuilder( ourPB, ourPB->buffer, gFloppyFormatCapacityParam, gFloppyFormatBlockSizeParam, gCurrentFloppyFormatTrack, 0);
- }
- break;
-
- case kFormatFloppyFormatTrackSide0:
- {
- // Check if an error occurred
- if ( err != noErr )
- {
- break;
- }
-
- // Increment the track counter
- gCurrentFloppyFormatTrack++;
-
- // Check if all tracks are done
- if ( gCurrentFloppyFormatTrack >= 0x50)
- {
- // We have written all tracks on both sides
- // we are done, signal to do a client callback
- err = noErr;
- break;
- }
-
- // Format current track on side 1
- ourPB->currentExecutionState = kFormatFloppyFormatTrackSide1;
- ourPB->completionProc = &FormatFloppyStateMachine;
- err = FormatFloppyTrackBuilder( ourPB, ourPB->buffer, gFloppyFormatCapacityParam, gFloppyFormatBlockSizeParam, gCurrentFloppyFormatTrack, 0);
- }
- break;
-
- case kFormatFloppyFormatTrackSide1:
- {
- // Check if an error occurred
- if ( err != noErr )
- {
- break;
- }
-
- // Format current track on side 1
- ourPB->currentExecutionState = kFormatFloppyFormatTrackSide0;
- ourPB->completionProc = &FormatFloppyStateMachine;
- err = FormatFloppyTrackBuilder( ourPB, ourPB->buffer, gFloppyFormatCapacityParam, gFloppyFormatBlockSizeParam, gCurrentFloppyFormatTrack, 1);
- }
- break;
- }
-
- if ( err != kRequestPending )
- {
- if ( err == noErr )
- {
- // No errors occurred, on to the next part of
- // the process.
- ourPB->completionProc = &FormatMediaComplete;
- ourPB->currentExecutionState = kFormatDoneState;
- ourPB->status = kRequestPending;
- FormatMediaComplete ( ourPB );
- }
- else
- {
- // An error occurred, return error back to client.
- UInt32 theUserData;
- DefaultDAMCompletionProcPtr theCallBack;
-
- theUserData = ourPB->userData;
- theCallBack = (DefaultDAMCompletionProcPtr) ourPB->userCompletionProc;
- FreeInternalPB( ourPB );
- (*theCallBack) ( theUserData, err);
- }
- }
- }
-
-
-